package com.ssy.lingxi.member.merchant.serviceimpl.mobile;

import com.ssy.lingxi.common.constant.basic.EnableDisableStatus;
import com.ssy.lingxi.common.constant.basic.UserLoginSourceEnum;
import com.ssy.lingxi.common.constant.manage.ShopTypeEnum;
import com.ssy.lingxi.common.constant.member.MemberLevelTypeEnum;
import com.ssy.lingxi.common.constant.member.MemberStatusEnum;
import com.ssy.lingxi.common.constant.member.MemberTypeEnum;
import com.ssy.lingxi.common.constant.member.RoleTypeEnum;
import com.ssy.lingxi.common.model.dto.MemberAndRoleIdDTO;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.common.utils.PasswordUtil;
import com.ssy.lingxi.common.utils.TokenUtil;
import com.ssy.lingxi.member.merchant.config.ServiceConfig;
import com.ssy.lingxi.member.merchant.config.ServiceVariable;
import com.ssy.lingxi.member.merchant.entity.MemberDO;
import com.ssy.lingxi.member.merchant.entity.MemberRelationDO;
import com.ssy.lingxi.member.merchant.entity.MemberUserDO;
import com.ssy.lingxi.member.merchant.entity.RoleDO;
import com.ssy.lingxi.member.merchant.model.bo.MobileLoginBO;
import com.ssy.lingxi.member.merchant.api.constant.MemberRelationTypeEnum;
import com.ssy.lingxi.member.merchant.model.constant.MemberValidateStatusEnum;
import com.ssy.lingxi.member.merchant.model.vo.basic.request.PhoneVO;
import com.ssy.lingxi.member.merchant.model.vo.login.request.MobileLoginVO;
import com.ssy.lingxi.member.merchant.model.vo.login.request.MobilePhoneLoginVO;
import com.ssy.lingxi.member.merchant.model.vo.login.request.MobileShopTypeVO;
import com.ssy.lingxi.member.merchant.model.vo.login.request.MobileSwitchRoleVO;
import com.ssy.lingxi.member.merchant.model.vo.mobile.request.MobileMemberAuthCodeVO;
import com.ssy.lingxi.member.merchant.repository.MemberRelationRepository;
import com.ssy.lingxi.member.merchant.repository.MemberUserRepository;
import com.ssy.lingxi.member.merchant.service.base.IBaseMemberCacheService;
import com.ssy.lingxi.member.merchant.service.base.IBaseMemberCreditAsyncService;
import com.ssy.lingxi.member.merchant.service.base.IBaseMemberLevelAsyncService;
import com.ssy.lingxi.member.merchant.service.feign.ISmsFeignService;
import com.ssy.lingxi.member.merchant.service.mobile.IMobileLoginService;
import com.ssy.lingxi.member.merchant.utils.AuthBOUtils;
import com.ssy.lingxi.member.merchant.utils.SecurityStringUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpHeaders;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * App - 用户登录接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2020-12-05
 */
@Service
public class MobileLoginServiceImpl implements IMobileLoginService {
    @Resource
    private MemberUserRepository memberUserRepository;

    @Resource
    private MemberRelationRepository relationRepository;

    @Resource
    private IBaseMemberCacheService memberCacheService;

    @Resource
    private IBaseMemberLevelAsyncService baseMemberLevelAsyncService;

    @Resource
    private IBaseMemberCreditAsyncService baseMemberCreditAsyncService;

    @Resource
    private ISmsFeignService smsFeignService;

    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 账号或手机号登录
     * @param headers Http头部信息
     * @param loginVO 接口参数
     * @return 登录用户信息
     */
    @Override
    public Wrapper<MobileLoginBO> accountOrPhoneLogin(HttpHeaders headers, MobileLoginVO loginVO) {
        memberCacheService.checkMobileRequestHeader(headers);

        //从用户表中读取用户信息
        MemberUserDO memberUserDO = memberUserRepository.findLoginUser(loginVO.getAccount(), loginVO.getAccount(), MemberRelationTypeEnum.OTHER.getCode());
        if (memberUserDO == null || memberUserDO.getStatus().equals(EnableDisableStatus.DISABLE.getCode())) {
            return Wrapper.fail(ResponseCode.USER_ACCOUNT_OR_PASSWORD_INCORRECT);
        }

        MemberDO memberDO = memberUserDO.getMember();
        if (memberDO == null) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_DOES_NOT_EXIST);
        }

        //商城类型
        ShopTypeEnum shopType = ShopTypeEnum.parseCode(loginVO.getShopType());
        if (shopType == null) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_SHOP_MISMATCHED);
        }

        //校验密码
        if (!PasswordUtil.instance().checkPassword(memberUserDO.getPassword(), loginVO.getPassword())) {
            return Wrapper.fail(ResponseCode.USER_ACCOUNT_OR_PASSWORD_INCORRECT);
        }

        Long siteId = memberCacheService.getLongValueFromRequestHeader(headers, ServiceConfig.USER_LOGIN_SITE_HTTP_HEADERS_KEY);
        Long shopId = memberCacheService.getLongValueFromRequestHeader(headers, ServiceConfig.USER_LOGIN_SHOP_HTTP_HEADERS_KEY);

        return mobileLogin(memberDO, memberUserDO, shopType, siteId, shopId);
    }


    /**
     * 发送“手机号登录”的短信验证码
     *
     * @param headers Http头部信息
     * @param phoneVO 接口参数
     * @return 发送结果
     */
    @Override
    public Wrapper<Void> sendPhoneLoginSmsCode(HttpHeaders headers, PhoneVO phoneVO) {
        memberCacheService.checkMobileRequestHeader(headers);

        MemberUserDO memberUserDO = memberUserRepository.findFirstByCountryCodeAndPhoneAndRelType(phoneVO.getCountryCode(), phoneVO.getPhone(), MemberRelationTypeEnum.OTHER.getCode());

        if (memberUserDO == null) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_USER_DOES_NOT_EXIST);
        }

        if (memberUserDO.getMember() == null) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_USER_DOES_NOT_EXIST);
        }

        List<MemberRelationDO> relationDOList = relationRepository.findBySubMemberIdAndRelType(memberUserDO.getMember().getId(), MemberRelationTypeEnum.PLATFORM.getCode());
        if (relationDOList.stream().noneMatch(relationDO -> relationDO.getStatus().equals(MemberStatusEnum.NORMAL.getCode()))) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_HAS_BEEN_FROZEN);
        }

        String smsCode = SecurityStringUtils.getRandomSmsCode();
        String cacheKey = ServiceConfig.MOBILE_PHONE_LOGIN_SMS_CODE_REDIS_KEY_PREFIX + memberUserDO.getId();
        memberCacheService.setString(cacheKey, smsCode, ServiceConfig.MOBILE_PHONE_LOGIN_SMS_CACHED_TIME_MINUTES * 60);

        return smsFeignService.sendMobilePhoneLoginSms(memberUserDO.getCountryCode(), memberUserDO.getPhone(), smsCode, ServiceVariable.SMSCODE_CACHED_TIME_MINUTES);
    }

    /**
     * 手机号登录
     * @param headers Http头部信息
     * @param loginVO 接口参数
     * @return 登录用户信息
     */
    @Override
    public Wrapper<MobileLoginBO> phoneLogin(HttpHeaders headers, MobilePhoneLoginVO loginVO) {
        memberCacheService.checkMobileRequestHeader(headers);

        //商城类型
        ShopTypeEnum shopType = ShopTypeEnum.parseCode(loginVO.getShopType());
        if (shopType == null) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_SHOP_MISMATCHED);
        }

        //从用户表中读取用户信息
        MemberUserDO memberUserDO = memberUserRepository.findFirstByCountryCodeAndPhoneAndRelType(loginVO.getCountryCode(), loginVO.getPhone(), MemberRelationTypeEnum.OTHER.getCode());
        if (memberUserDO == null || memberUserDO.getStatus().equals(EnableDisableStatus.DISABLE.getCode())) {
            return Wrapper.fail(ResponseCode.USER_ACCOUNT_OR_PASSWORD_INCORRECT);
        }

        MemberDO memberDO = memberUserDO.getMember();
        if (memberDO == null) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_DOES_NOT_EXIST);
        }

        //校验短信验证码
        String cacheKey = ServiceConfig.MOBILE_PHONE_LOGIN_SMS_CODE_REDIS_KEY_PREFIX + memberUserDO.getId();
        String smsCode = memberCacheService.getString(cacheKey);
        if (!StringUtils.hasLength(smsCode)) {
            return Wrapper.fail(ResponseCode.USER_SMSCODE_EXPIRED);
        }

        if (!loginVO.getSmsCode().equals(smsCode)) {
            return Wrapper.fail(ResponseCode.USER_SMSCODE_INCORRECT);
        }

        Long siteId = memberCacheService.getLongValueFromRequestHeader(headers, ServiceConfig.USER_LOGIN_SITE_HTTP_HEADERS_KEY);
        Long shopId = memberCacheService.getLongValueFromRequestHeader(headers, ServiceConfig.USER_LOGIN_SHOP_HTTP_HEADERS_KEY);

        return mobileLogin(memberDO, memberUserDO, shopType, siteId, shopId);
    }

    /**
     * 移动端登录接口
     * @param memberDO 登录的会员
     * @param memberUserDO 登录的用户
     * @param shopType 商城类型
     * @param siteId 商城类型
     * @param shopId 商城类型
     * @return 登录用户信息
     */
    private Wrapper<MobileLoginBO> mobileLogin(MemberDO memberDO, MemberUserDO memberUserDO, ShopTypeEnum shopType, Long siteId, Long shopId) {
        //登录会员上下级会员的关系列表（查询服务消费者角色）
        Specification<MemberRelationDO> specification = (Specification<MemberRelationDO>) (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.or(criteriaBuilder.equal(root.get("memberId").as(Long.class), memberDO.getId()), criteriaBuilder.equal(root.get("subMemberId").as(Long.class), memberDO.getId())));
            list.add(criteriaBuilder.or(criteriaBuilder.equal(root.get("relType").as(Integer.class), MemberRelationTypeEnum.PLATFORM.getCode()),
                    criteriaBuilder.and(criteriaBuilder.equal(root.get("relType").as(Integer.class), MemberRelationTypeEnum.OTHER.getCode()),
                            criteriaBuilder.equal(root.get("verified").as(Integer.class), MemberValidateStatusEnum.VERIFY_PASSED.getCode()),
                            criteriaBuilder.equal(root.get("status").as(Integer.class), MemberStatusEnum.NORMAL.getCode()))));

            Join<MemberRelationDO, RoleDO> subRoleJoin = root.join("subRole", JoinType.LEFT);
            list.add(criteriaBuilder.equal(subRoleJoin.get("status").as(Integer.class), EnableDisableStatus.ENABLE.getCode()));
            list.add(criteriaBuilder.equal(subRoleJoin.get("roleType").get("typeEnum").as(Integer.class), RoleTypeEnum.SERVICE_CONSUMER.getCode()));

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        //如果关系为空，表示没有消费者角色
        List<MemberRelationDO> relationDOList = relationRepository.findAll(specification);
        if (CollectionUtils.isEmpty(relationDOList)) {
            return Wrapper.fail(ResponseCode.MC_MS_USER_HAS_NO_AVAILABLE_CONSUMER_ROLE);
        }

        //找出所有可用的“服务消费者”角色，这些角色的状态为：审核通过、未冻结、平台会员状态正常、角色状态正常
        //如果当前选择的商城类型是企业商城则只显示会员类型为企业会员或个人会员的且角色类型为服务消费的会员角色
        //如果当前选择的商城类型是渠道商城则只显示会员类型为渠道企业会员或渠道个人会员的且角色类型为服务消费的会员角色
        //如果当前选择的商城类型是渠道自有商城则只显示会员类型为渠道企业会员或渠道个人会员的且角色类型为服务消费的会员角色
        List<MemberRelationDO> availablePlatformRelations = new ArrayList<>();
        List<MobileLoginBO.UpperChannelItem> upperChannels = new ArrayList<>();
        switch (shopType) {
            case ENTERPRISE:
                availablePlatformRelations = relationDOList.stream().filter(relationDO -> relationDO.getSubMemberId().equals(memberDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.PLATFORM.getCode()) && (relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.MERCHANT.getCode()) || relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.MERCHANT_PERSONAL.getCode()))).collect(Collectors.toList());
                break;
            case CHANNEL:
            case CHANNEL_SELF:
                availablePlatformRelations = relationDOList.stream().filter(relationDO -> relationDO.getSubMemberId().equals(memberDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.PLATFORM.getCode()) && (relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL.getCode()) || relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL_PERSONAL.getCode()))).collect(Collectors.toList());
                //选择商城类型为渠道商城/渠道自有商城，查询上级渠道
                upperChannels = relationDOList.stream().filter(relation -> relation.getSubMemberId().equals(memberDO.getId()) && relation.getSubMemberLevelTypeEnum().equals(MemberLevelTypeEnum.CHANNEL.getCode())).sorted(Comparator.comparingLong(MemberRelationDO::getId)).map(relation -> new MobileLoginBO.UpperChannelItem(relation.getMemberId(), relation.getRoleId(), relation.getMember().getName())).collect(Collectors.toList());
                break;
            default:
                break;
        }

        //如果可用平台会员为空，返回无可用消费者角色
        if (CollectionUtils.isEmpty(availablePlatformRelations)) {
            return Wrapper.fail(ResponseCode.MC_MS_USER_HAS_NO_AVAILABLE_CONSUMER_ROLE);
        }

        //如果所有平台关系已经被冻结，返回
        availablePlatformRelations = availablePlatformRelations.stream().filter(relationDO -> relationDO.getStatus().equals(MemberStatusEnum.NORMAL.getCode())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(availablePlatformRelations)) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_HAS_BEEN_FROZEN);
        }

        MemberRelationDO loginPlatformRelationDO = availablePlatformRelations.get(0);

        List<RoleDO> availableRoles = availablePlatformRelations.stream().map(MemberRelationDO::getSubRole).collect(Collectors.toList());

        //所有角色列表
        List<MobileLoginBO.MemberRoleItem> roleList = availableRoles.stream().map(roleDO -> new MobileLoginBO.MemberRoleItem(roleDO.getId(), roleDO.getRoleName(), roleDO.getRoleType().getTypeEnum())).sorted(Comparator.comparingLong(MobileLoginBO.MemberRoleItem::getRoleId)).collect(Collectors.toList());

        //登录的角色
        RoleDO roleDO = loginPlatformRelationDO.getSubRole();
        //会员等级
        Integer level = loginPlatformRelationDO.getLevelRight().getLevel();

        String token = TokenUtil.instance().getToken();

        //授权访问的前端菜单Url列表，如果审核没通过，只能访问首页
        boolean verifyPassed = loginPlatformRelationDO.getVerified().equals(MemberValidateStatusEnum.VERIFY_PASSED.getCode());
        List<String> urlList = verifyPassed ? AuthBOUtils.getAccessUrlList(memberUserDO.getUserAuth().getAuth(), loginPlatformRelationDO.getRelationAuth().getAuth(), UserLoginSourceEnum.BUSINESS_MOBILE.getCode(), ServiceConfig.MEMBER_MAIN_APP_PAGE_CODES) : ServiceConfig.MEMBER_MAIN_APP_PAGE_CODES;

        //上级渠道会员的Id列表
        List<Long> upperChannelMemberIds = relationDOList.stream().filter(relationDO -> relationDO.getSubMemberId().equals(memberDO.getId()) && relationDO.getSubRoleId().equals(roleDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.OTHER.getCode()) && relationDO.getRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL.getCode()) || relationDO.getRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL_PERSONAL.getCode())).map(MemberRelationDO::getMemberId).collect(Collectors.toList());

        //上级供应商
        List<MemberAndRoleIdDTO> upperVendorMembers = roleDO.getRoleType().getTypeEnum().equals(RoleTypeEnum.SERVICE_PROVIDER.getCode()) ? relationDOList.stream().filter(relationDO -> relationDO.getSubMemberId().equals(memberDO.getId()) && relationDO.getSubRoleId().equals(roleDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.OTHER.getCode()) && relationDO.getRole().getRoleType().getTypeEnum().equals(RoleTypeEnum.SERVICE_PROVIDER.getCode())).map(relationDO -> new MemberAndRoleIdDTO(relationDO.getMemberId(), relationDO.getRoleId())).collect(Collectors.toList()) : new ArrayList<>();

        //下级供应商
        List<MemberAndRoleIdDTO> subVendorMembers = roleDO.getRoleType().getTypeEnum().equals(RoleTypeEnum.SERVICE_PROVIDER.getCode()) ? relationDOList.stream().filter(relationDO -> relationDO.getMemberId().equals(memberDO.getId()) && relationDO.getRoleId().equals(roleDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.OTHER.getCode()) && relationDO.getSubRole().getRoleType().getTypeEnum().equals(RoleTypeEnum.SERVICE_PROVIDER.getCode())).map(relationDO -> new MemberAndRoleIdDTO(relationDO.getSubMemberId(), relationDO.getSubRoleId())).collect(Collectors.toList()) : new ArrayList<>();

        //下级渠道会员Id和角色Id列表
        List<MemberRelationDO> subChannelRelationDOList = relationDOList.stream().filter(relationDO -> relationDO.getMemberId().equals(memberDO.getId()) && relationDO.getRoleId().equals(roleDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.OTHER.getCode())).collect(Collectors.toList());
        List<MemberAndRoleIdDTO> subChannelMembers = subChannelRelationDOList.stream().filter(relationDO -> relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL.getCode()) || relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL_PERSONAL.getCode())).map(relationDO -> new MemberAndRoleIdDTO(relationDO.getSubMemberId(), relationDO.getSubRoleId())).collect(Collectors.toList());

        // 根据当前登录角色判断等级类型
        Integer memberLevelTypeEnum = MemberLevelTypeEnum.getCodeByMemberType(roleDO.getMemberType().getTypeEnum());

        //缓存用户信息
        memberCacheService.cacheUserInfo(UserLoginSourceEnum.BUSINESS_MOBILE, memberLevelTypeEnum, memberUserDO, memberDO, roleDO, upperChannelMemberIds, upperVendorMembers, subVendorMembers, subChannelMembers, new ArrayList<>(), token, siteId, shopId,level);

        //返回结果
        MobileLoginBO mobileLoginBO = new MobileLoginBO(loginPlatformRelationDO.getOuterStatus(), memberDO, memberUserDO, roleDO.getId(), roleDO.getMemberType().getTypeEnum(), token, ServiceConfig.MOBILE_LOGIN_TOKEN_EXPIRE_MINUTES, urlList, roleList, upperChannels);

        //异步： 计算会员登录获得的积分、会员注册年限获得的信用积分
        baseMemberLevelAsyncService.calculateMemberLoginScore(loginPlatformRelationDO, UserLoginSourceEnum.BUSINESS_WEB.getCode());

        baseMemberCreditAsyncService.calculateMemberRegisterYearsCredit(relationDOList.stream().filter(relationDO -> relationDO.getSubMemberId().equals(memberDO.getId()) && relationDO.getSubRoleId().equals(roleDO.getId())).collect(Collectors.toList()));

        //记录最后一次登录时间
        String sql = String.format("update ms_mc_member_user set last_login_time = '%s' where id = %d", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), memberUserDO.getId());
        jdbcTemplate.update(sql);

        return Wrapper.success(mobileLoginBO);
    }

    /**
     * 重新获取登录用户信息
     * @param headers Http头部信息
     * @param shopTypeVO 商城类型
     * @return 登录用户信息
     */
    @Override
    public Wrapper<MobileLoginBO> loginWithToken(HttpHeaders headers, MobileShopTypeVO shopTypeVO) {
        UserLoginCacheDTO loginUser = memberCacheService.needLoginFromMobile(headers);

        //商城类型
        ShopTypeEnum shopType = ShopTypeEnum.parseCode(shopTypeVO.getShopType());
        if (shopType == null) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_SHOP_MISMATCHED);
        }

        //登录来源
        if (!loginUser.getLoginSource().equals(UserLoginSourceEnum.BUSINESS_MOBILE.getCode())) {
            return Wrapper.fail(ResponseCode.NEED_LOGIN_FROM_BUSINESS_APP);
        }

        String token = headers.getFirst(ServiceConfig.USER_TOKEN_HTTP_HEADERS_KEY);

        Long siteId = memberCacheService.getLongValueFromRequestHeader(headers, ServiceConfig.USER_LOGIN_SITE_HTTP_HEADERS_KEY);
        Long shopId = memberCacheService.getLongValueFromRequestHeader(headers, ServiceConfig.USER_LOGIN_SHOP_HTTP_HEADERS_KEY);

        return reLoginWithTokenOrSpecifyRoleId(token, loginUser.getUserId(), loginUser.getMemberRoleId(), shopType, siteId, shopId);
    }

    /**
     * 会员登录后，选择角色
     *
     * @param headers Http头部信息
     * @param roleVO  接口参数
     * @return 登录用户信息
     */
    @Override
    public Wrapper<MobileLoginBO> switchLoginRole(HttpHeaders headers, MobileSwitchRoleVO roleVO) {
        UserLoginCacheDTO loginUser = memberCacheService.needLoginFromMobile(headers);

        //商城类型
        ShopTypeEnum shopType = ShopTypeEnum.parseCode(roleVO.getShopType());
        if (shopType == null) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_SHOP_MISMATCHED);
        }

        //登录来源
        if (!loginUser.getLoginSource().equals(UserLoginSourceEnum.BUSINESS_MOBILE.getCode())) {
            return Wrapper.fail(ResponseCode.NEED_LOGIN_FROM_BUSINESS_APP);
        }

        String token = headers.getFirst(ServiceConfig.USER_TOKEN_HTTP_HEADERS_KEY);

        Long siteId = memberCacheService.getLongValueFromRequestHeader(headers, ServiceConfig.USER_LOGIN_SITE_HTTP_HEADERS_KEY);
        Long shopId = memberCacheService.getLongValueFromRequestHeader(headers, ServiceConfig.USER_LOGIN_SHOP_HTTP_HEADERS_KEY);

        return reLoginWithTokenOrSpecifyRoleId(token, loginUser.getUserId(), roleVO.getRoleId(), shopType, siteId, shopId);
    }


    /**
     * 使用Token重新获取登录信息，登录后切换角色两个接口的通用方法
     * @param token          登录后的用户token
     * @param memberUserId   登录的用户Id
     * @param roleId         要切换的角色Id
     * @param shopType       商城类型
     * @param siteId         商城类型
     * @param shopId         商城类型
     * @return 登录信息
     */
    private Wrapper<MobileLoginBO> reLoginWithTokenOrSpecifyRoleId(String token, Long memberUserId, Long roleId, ShopTypeEnum shopType, Long siteId, Long shopId) {
        //从数据库中读取用户信息
        MemberUserDO memberUserDO = memberUserRepository.findById(memberUserId).orElse(null);
        if (memberUserDO == null || memberUserDO.getStatus().equals(EnableDisableStatus.DISABLE.getCode())) {
            return Wrapper.fail(ResponseCode.USER_ACCOUNT_OR_PASSWORD_INCORRECT);
        }

        MemberDO memberDO = memberUserDO.getMember();
        if (memberDO == null) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_DOES_NOT_EXIST);
        }

        //登录会员上下级会员的关系列表（查询服务消费者角色）
        Specification<MemberRelationDO> specification = (Specification<MemberRelationDO>) (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.or(criteriaBuilder.equal(root.get("memberId").as(Long.class), memberDO.getId()), criteriaBuilder.equal(root.get("subMemberId").as(Long.class), memberDO.getId())));
            list.add(criteriaBuilder.or(criteriaBuilder.equal(root.get("relType").as(Integer.class), MemberRelationTypeEnum.PLATFORM.getCode()),
                    criteriaBuilder.and(criteriaBuilder.equal(root.get("relType").as(Integer.class), MemberRelationTypeEnum.OTHER.getCode()),
                            criteriaBuilder.equal(root.get("verified").as(Integer.class), MemberValidateStatusEnum.VERIFY_PASSED.getCode()),
                            criteriaBuilder.equal(root.get("status").as(Integer.class), MemberStatusEnum.NORMAL.getCode()))));

            Join<MemberRelationDO, RoleDO> subRoleJoin = root.join("subRole", JoinType.LEFT);
            list.add(criteriaBuilder.equal(subRoleJoin.get("status").as(Integer.class), EnableDisableStatus.ENABLE.getCode()));
            list.add(criteriaBuilder.equal(subRoleJoin.get("roleType").get("typeEnum").as(Integer.class), RoleTypeEnum.SERVICE_CONSUMER.getCode()));

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        //如果关系为空，表示没有消费者角色
        List<MemberRelationDO> relationDOList = relationRepository.findAll(specification);
        if (CollectionUtils.isEmpty(relationDOList)) {
            return Wrapper.fail(ResponseCode.MC_MS_USER_HAS_NO_AVAILABLE_CONSUMER_ROLE);
        }

        //找出所有可用的“服务消费者”角色，这些角色的状态为：审核通过、未冻结、平台会员状态正常、角色状态正常
        //如果当前选择的商城类型是企业商城则只显示会员类型为企业会员或个人会员的且角色类型为服务消费的会员角色
        //如果当前选择的商城类型是渠道商城则只显示会员类型为渠道企业会员或渠道个人会员的且角色类型为服务消费的会员角色
        //如果当前选择的商城类型是渠道自有商城则只显示会员类型为渠道企业会员或渠道个人会员的且角色类型为服务消费的会员角色
        List<MemberRelationDO> availablePlatformRelations = new ArrayList<>();
        List<MobileLoginBO.UpperChannelItem> upperChannels = new ArrayList<>();
        switch (shopType) {
            case ENTERPRISE:
                availablePlatformRelations = relationDOList.stream().filter(relationDO -> relationDO.getSubMemberId().equals(memberDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.PLATFORM.getCode()) && (relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.MERCHANT.getCode()) || relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.MERCHANT_PERSONAL.getCode()))).collect(Collectors.toList());
                break;
            case CHANNEL:
            case CHANNEL_SELF:
                availablePlatformRelations = relationDOList.stream().filter(relationDO -> relationDO.getSubMemberId().equals(memberDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.PLATFORM.getCode()) && (relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL.getCode()) || relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL_PERSONAL.getCode()))).collect(Collectors.toList());
                //选择商城类型为渠道商城/渠道自有商城，查询上级渠道
                upperChannels = relationDOList.stream().filter(relation -> relation.getSubMemberId().equals(memberDO.getId()) && relation.getSubMemberLevelTypeEnum().equals(MemberLevelTypeEnum.CHANNEL.getCode())).sorted(Comparator.comparingLong(MemberRelationDO::getId)).map(relation -> new MobileLoginBO.UpperChannelItem(relation.getMemberId(), relation.getRoleId(), relation.getMember().getName())).collect(Collectors.toList());
                break;
            default:
                break;
        }

        //如果可用平台会员为空，返回无可用消费者角色
        if (CollectionUtils.isEmpty(availablePlatformRelations)) {
            return Wrapper.fail(ResponseCode.MC_MS_USER_HAS_NO_AVAILABLE_CONSUMER_ROLE);
        }

        //如果所有平台关系已经被冻结，返回
        availablePlatformRelations = availablePlatformRelations.stream().filter(relationDO -> relationDO.getStatus().equals(MemberStatusEnum.NORMAL.getCode())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(availablePlatformRelations)) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_HAS_BEEN_FROZEN);
        }

        //如果当前登录的角色已经被冻结，返回
        MemberRelationDO loginPlatformRelationDO = availablePlatformRelations.stream().filter(relationDO -> relationDO.getSubMemberId().equals(memberDO.getId()) && relationDO.getSubRoleId().equals(roleId)).findFirst().orElse(null);
        if (loginPlatformRelationDO == null || !loginPlatformRelationDO.getStatus().equals(MemberStatusEnum.NORMAL.getCode())) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_HAS_BEEN_FROZEN);
        }

        //登录的角色
        RoleDO roleDO = loginPlatformRelationDO.getSubRole();
        //会员等级
        Integer level = loginPlatformRelationDO.getLevelRight().getLevel();

        //筛选出平台审核通过的、未冻结、状态为启用、的角色
        List<RoleDO> availableRoles = availablePlatformRelations.stream().filter(relationDO -> relationDO.getStatus().equals(MemberStatusEnum.NORMAL.getCode())).map(MemberRelationDO::getSubRole).collect(Collectors.toList());

        List<MobileLoginBO.MemberRoleItem> roleList = availableRoles.stream().map(role -> new MobileLoginBO.MemberRoleItem(role.getId(), role.getRoleName(), role.getRoleType().getTypeEnum())).sorted(Comparator.comparingLong(MobileLoginBO.MemberRoleItem::getRoleId)).collect(Collectors.toList());

        //授权访问的前端菜单Url列表，如果审核没通过，只能访问首页
        boolean verifyPassed = loginPlatformRelationDO.getVerified().equals(MemberValidateStatusEnum.VERIFY_PASSED.getCode());
        List<String> urlList = verifyPassed ? AuthBOUtils.getAccessUrlList(memberUserDO.getUserAuth().getAuth(), loginPlatformRelationDO.getRelationAuth().getAuth(), UserLoginSourceEnum.BUSINESS_MOBILE.getCode(), ServiceConfig.MEMBER_MAIN_APP_PAGE_CODES) : ServiceConfig.MEMBER_MAIN_APP_PAGE_CODES;

        //上级渠道会员的Id列表
        List<Long> upperChannelMemberIds = relationDOList.stream().filter(relationDO -> relationDO.getSubMemberId().equals(memberDO.getId()) && relationDO.getSubRoleId().equals(roleDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.OTHER.getCode()) && relationDO.getRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL.getCode()) || relationDO.getRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL_PERSONAL.getCode())).map(MemberRelationDO::getMemberId).collect(Collectors.toList());

        //上级供应商
        List<MemberAndRoleIdDTO> upperVendorMembers = roleDO.getRoleType().getTypeEnum().equals(RoleTypeEnum.SERVICE_PROVIDER.getCode()) ? relationDOList.stream().filter(relationDO -> relationDO.getSubMemberId().equals(memberDO.getId()) && relationDO.getSubRoleId().equals(roleDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.OTHER.getCode()) && relationDO.getRole().getRoleType().getTypeEnum().equals(RoleTypeEnum.SERVICE_PROVIDER.getCode())).map(relationDO -> new MemberAndRoleIdDTO(relationDO.getMemberId(), relationDO.getRoleId())).collect(Collectors.toList()) : new ArrayList<>();

        //下级供应商
        List<MemberAndRoleIdDTO> subVendorMembers = roleDO.getRoleType().getTypeEnum().equals(RoleTypeEnum.SERVICE_PROVIDER.getCode()) ? relationDOList.stream().filter(relationDO -> relationDO.getMemberId().equals(memberDO.getId()) && relationDO.getRoleId().equals(roleDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.OTHER.getCode()) && relationDO.getSubRole().getRoleType().getTypeEnum().equals(RoleTypeEnum.SERVICE_PROVIDER.getCode())).map(relationDO -> new MemberAndRoleIdDTO(relationDO.getSubMemberId(), relationDO.getSubRoleId())).collect(Collectors.toList()) : new ArrayList<>();

        //下级渠道会员Id和角色Id列表
        List<MemberRelationDO> subChannelRelationDOList = relationDOList.stream().filter(relationDO -> relationDO.getMemberId().equals(memberDO.getId()) && relationDO.getRoleId().equals(roleDO.getId()) && relationDO.getRelType().equals(MemberRelationTypeEnum.OTHER.getCode())).collect(Collectors.toList());
        List<MemberAndRoleIdDTO> subChannelMembers = subChannelRelationDOList.stream().filter(relationDO -> relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL.getCode()) || relationDO.getSubRole().getMemberType().getTypeEnum().equals(MemberTypeEnum.CHANNEL_PERSONAL.getCode())).map(relationDO -> new MemberAndRoleIdDTO(relationDO.getSubMemberId(), relationDO.getSubRoleId())).collect(Collectors.toList());

        // 根据当前登录角色判断等级类型
        Integer memberLevelTypeEnum = MemberLevelTypeEnum.getCodeByMemberType(roleDO.getMemberType().getTypeEnum());

        //缓存用户信息
        memberCacheService.cacheUserInfo(UserLoginSourceEnum.BUSINESS_MOBILE, memberLevelTypeEnum, memberUserDO, memberDO, roleDO, upperChannelMemberIds, upperVendorMembers, subVendorMembers, subChannelMembers, new ArrayList<>(), token, siteId, shopId, level);

        //返回结果
        MobileLoginBO mobileLoginBO = new MobileLoginBO(loginPlatformRelationDO.getOuterStatus(), memberDO, memberUserDO, roleDO.getId(), roleDO.getMemberType().getTypeEnum(), token, ServiceConfig.MOBILE_LOGIN_TOKEN_EXPIRE_MINUTES, urlList, roleList, upperChannels);

        return Wrapper.success(mobileLoginBO);
    }

    @Override
    public Wrapper<Void> authCodeActive(HttpHeaders headers, MobileMemberAuthCodeVO memberAuthCodeVO) {
        // 检查手机是否登录
        UserLoginCacheDTO userLoginCacheDTO = memberCacheService.needLoginFromMobile(headers);

        memberCacheService.activeAuthCode(memberAuthCodeVO.getAuthCode(), userLoginCacheDTO);

        return Wrapper.success();
    }
}
